//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation

Module Name:

callctrl.cpp

Abstract:


Notes:


--*/
/*
------------------------------------------------------------------------------
    Revision History
    Date                Author          Activity ID              Activity Headline
    2006-04-22          qianyili        CEDB00015330             Move RIL from public to bsp270
    2007-01-08          qinxuetao       CEDB00040729             Porting ril driver for CC/SMS
    2007-02-08          qinxuetao       CEDB200043651            Update ril driver for network and data service
    2007-04-19          gaorenhui       WM600002982              Porting code from ORCA for WM600002982.
    2007-05-10          qinxuetao       WM600003334              If muting has been set, Dragonfly needs setting again after any call is retrieved or connected.
    2007-05-10          qinxuetao       WM600003334              While line state is ringing, set MM_WOM_FORCESPEAKER to audio driver
    2007-07-26          qinxuetao       WM600011278              Fixing CSD call issues
    2007-09-05          zhongchuren     WM600015327              [AIS]fix bruetooth earphone no sound issue
    2007-10-12          Wangxiaogang    WM600019541              Add PIN required UI when dialing
    2007-11-28          Wangxiaogang    WM600024426              Modify CallerID function
------------------------------------------------------------------------------
*/


#include "precomp.h"
#include <rilbacklight.h>
#include "rilhand.h"
#include <wchar.h>

//
// Call status values
//
static const DWORD g_rgdwCallStats[] =
{
    RIL_CALLSTAT_ACTIVE,            // 0
    RIL_CALLSTAT_ONHOLD,            // 1
    RIL_CALLSTAT_DIALING,           // 2
    RIL_CALLSTAT_ALERTING,          // 3
    RIL_CALLSTAT_INCOMING,          // 4
    RIL_CALLSTAT_WAITING,           // 5
};
#define NUM_CALLSTATS   (sizeof(g_rgdwCallStats) / sizeof(DWORD))


//
// Call type values
//
static const DWORD g_rgdwCallTypes[] =
{
    RIL_CALLTYPE_VOICE,             // 0
    RIL_CALLTYPE_DATA,              // 1
    RIL_CALLTYPE_FAX,               // 2
    RIL_CALLTYPE_PTT,               // 3
    RIL_CALLTYPE_VT,                // 4
    RIL_CALLTYPE_UNKNOWN,           // 5
    RIL_CALLTYPE_UNKNOWN,           // 6
    RIL_CALLTYPE_UNKNOWN,           // 7
    RIL_CALLTYPE_UNKNOWN,           // 8
    RIL_CALLTYPE_UNKNOWN,           // 9
};
#define NUM_CALLTYPES   (sizeof(g_rgdwCallTypes) / sizeof(DWORD))


//
// Line status values
//
static const DWORD g_rgdwLineStats[] =
{
    RIL_LINESTAT_READY,             // 0
    RIL_LINESTAT_UNAVAILABLE,       // 1
    RIL_LINESTAT_UNKNOWN,           // 2
    RIL_LINESTAT_RINGING,           // 3
    RIL_LINESTAT_CALLINPROGRESS,    // 4
    RIL_LINESTAT_ASLEEP,            // 5
};
#define NUM_LINESTATS   (sizeof(g_rgdwLineStats) / sizeof(DWORD))

// Defines an arbitrary maximum string length for the dial command.
// This maximum is not strictly enforced. It is used only as an aid
// for reporting a more specific error (RIL_E_DIALSTRINGTOOLONG) if
// the radio returns an error that is too generic.
#define MAX_DIAL_STRING_LENGTH      43

BOOL g_rgfCallsInProgress[RIL_MAX_TRACKED_CALL_ID] = { FALSE };   // Initialize to FALSE
RILCALLINFO g_rgfCallStates[RIL_MAX_TRACKED_CALL_ID];

DWORD dwBTHarewareState;
BOOL    g_btctr_flag = FALSE;

extern BOOL g_rfExternalCalltypeDetermination;

BOOL g_rgfCalltypeChecked[RIL_MAX_TRACKED_CALL_ID] = { FALSE };   // Initialize to FALSE
DWORD g_rgctCalltype[RIL_MAX_TRACKED_CALL_ID] = { 0 };   // Initialize to 0

DIALEDCALLDATA g_rcdDialedCallData;
extern CRITICAL_SECTION g_csDialedCallData;

extern RINGINGCALLDATA g_rcdRingingCallData;
extern CRITICAL_SECTION g_csRingingCallData;
extern void RILSecSetPinRequiredFlag(bool flag);
extern BOOL RILSecGetPinRequiredFlag(void);
extern DWORD WINAPI PromptForSimAndRequeueCmdProc(LPVOID lpParameter);
extern DWORD g_dwLockFacility;
#ifdef EMP_DRIVER
// Define a global call type for call dialing, answering and hanging up
DWORD g_dwCallType = RIL_CALLTYPE_UNKNOWN;
#endif

VOID SetCalltypeFromCallInfo(RILCALLINFO *prci);

// BOOL g_rgfFirstParseCLCC;  // use only if g_rgfPreviousCallsInProgress and g_rgfPreviousCallStates are to be added

BOOL g_fHoldBeforDail = FALSE;  //porting from ORCA by gaorenhui

//
//
//
HRESULT RILDrv_Dial(DWORD dwParam, LPCSTR szAddress, DWORD dwType, DWORD dwOptions)
{
    FUNCTION_TRACE(RILDrv_Dial);
#if defined (NODIALING) && !defined (SHIP_BUILD)
    bool fNoDialing;
    bool fNoEmergencyDialing;
#endif // NODIALING

    CNotificationData* pnd = NULL;
    RILCONNECTINFO rci; memset(&rci,0,sizeof(rci)); // zero struct
    char szCmd[MAX_PATH];
    LPSTR szWalk = szCmd;
    LPSTR szDialStringStart = NULL;
    HRESULT hr = S_OK;
    LPCSTR szAddrWalk;
    DWORD dwDialOpts = CMDOPT_DIAL|CMDOPT_RETRYONSIMLOCKED;

    if(RILSecGetPinRequiredFlag())
    {
        HKEY hKey = NULL; 
        LPWSTR szBase;
        LPWSTR szFind;   
        BOOL fContinueSearch = TRUE;
        wchar_t wszAddress[sizeof(szCmd)] = {0};
        wchar_t wcSimEmergencyNum[100]={L""};
        DWORD dwType = 0;
        DWORD dwSize = sizeof(wcSimEmergencyNum);


        LONG lResult =RegOpenKeyEx(HKEY_LOCAL_MACHINE,ECC_REGISTER_VALUE_HAVE_SIM,0, 0,&hKey); 

         if ( lResult == ERROR_SUCCESS )
         {
              RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_Dial : ERROR_SUCCESS: RegOpenKeyEx\r\n")));

             lResult = RegQueryValueEx(hKey,L"List", NULL, &dwType, (LPBYTE) &wcSimEmergencyNum, &dwSize );
             if ( lResult == ERROR_SUCCESS )
             {
                if(dwType!=REG_MULTI_SZ)
                {
                        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_Dial : ERROR_SUCCESS: REG_MULTI_SZ\r\n")));
                        goto Error;
                }
            }
            RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_Dial : ERROR_SUCCESS: RegQueryValueE\r\n")));
            
        szBase = (LPWSTR)wcSimEmergencyNum;
        mbstowcs(wszAddress, szAddress, strlen(szAddress));
        do
        {
            szFind = wmemchr(szBase, L'\000', sizeof(wcSimEmergencyNum) - (szBase - wcSimEmergencyNum));
            if ((szFind == szBase)||(szFind == NULL))
            {
                fContinueSearch = FALSE;
                break;
            }
            if(0 == wcscmp(wszAddress, szBase))
            {
                // EMC
                break;
            }
            szBase = szFind + 1;
            if (szBase > wcSimEmergencyNum + sizeof(wcSimEmergencyNum) )
            {
                fContinueSearch = FALSE;
            }
        }while(fContinueSearch);
      }
      else
      {
             RETAILMSG(1, (TEXT("RILDrv : E : RILDrv_Dial : ERROR_SUCCESS: RegOpenKeyEx\r\n")));
            goto Error;
       }
        
        if(!fContinueSearch)
        {
                HANDLE hThread;
                     RETAILMSG(1, (TEXT("RILDrv : E : sunrenhong coming for dial normal call and check pin")));

                RILSecSetPinRequiredFlag(FALSE);
                g_dwLockFacility=SIM_LOCKFACILITY_SIM;
                hThread = CreateThread(NULL, 0, PromptForSimAndRequeueCmdProc, (LPVOID)NULL, 0, NULL);
                CloseHandle(hThread);
                goto Error;
          }
    }
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !szAddress) {
        hr = E_FAIL;
        goto Error;
    }

#if defined (NODIALING) && !defined (SHIP_BUILD)
    // See if we want to disable dialing
    if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NoDialing"), &fNoDialing) && fNoDialing) {
        // We can't allow any dialing (for stress tests)
#ifndef SHIP_BUILD
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_Dial : Dialing is prevented: %s\r\n"), TString(szAddress)));
#endif // !SHIP_BUILD
        hr = E_FAIL;
        goto Error;
    }

    // See if we want to disable emergency dialing
    if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NoEmergencyDialing"), &fNoEmergencyDialing) &&
        fNoEmergencyDialing) {
        if (!strcmp(szAddress, "911") || !strcmp(szAddress, "112") || !strcmp(szAddress, "08")) {
            // We can't allow dialing of emergency numbers (for stress tests)
#ifndef SHIP_BUILD
            RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_Dial : Dialing of an emergency number is prevented: %s\r\n"), TString(szAddress)));
#endif // !SHIP_BUILD
            hr = E_FAIL;
            goto Error;
        }
    }
#endif // NODIALING

    szWalk = BeginLineSpecificCommand(szCmd, MAX_PATH, 0);

#ifdef EMP_DRIVER
    // Write down current call type
    g_dwCallType = dwType;
#endif

    switch (dwType)
    {
        case RIL_CALLTYPE_VOICE:
        case RIL_CALLTYPE_PTT:
            pnd = new CNotificationData;
            if (pnd) {
                rci.cbSize = sizeof(RILCONNECTINFO);
                rci.dwParams = RIL_PARAM_CNI_CALLTYPE;
                rci.dwCallType = dwType;
                if (!pnd->InitFromRealBlob(RIL_NOTIFY_CONNECT, &rci, sizeof(RILCONNECTINFO))) {
                    delete pnd;
                    pnd = NULL;
                }
            }
            // fall through

        case RIL_CALLTYPE_DATA:
            strncpyz(szWalk,"D",MAX_PATH - (szWalk - szCmd));
            break;

        case RIL_CALLTYPE_VT:
#ifdef EMP_DRIVER
            // EMP uses "AT*EVD="<number>"\r" to place a VT call
            // Note that <number> is a quoted string
            strncpyz(szWalk,"*EVD=\"",MAX_PATH - (szWalk - szCmd));
            break ;
#else
            // VT is not supported on other platforms
            hr = E_NOTIMPL;
            goto Error;
#endif

        case RIL_CALLTYPE_FAX:
#ifdef EMP_DRIVER
            // EMP does not support FAX
            hr = E_NOTIMPL;
            goto Error;
#else
            (void)strncpyz(szWalk, "+FCLASS+1;D", MAX_PATH - (szWalk - szCmd));
#endif
            break;

        default:
            // This should have been caught in the proxy
            DEBUGCHK(FALSE);

        case RIL_CALLTYPE_UNKNOWN:
            hr = E_INVALIDARG;
            goto Error;
    }

    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    szDialStringStart = szWalk;
    if (dwOptions & RIL_DIALOPT_RESTRICTID) 
    {
        (void)strncpyz(szWalk, "#31#", MAX_PATH - (szWalk - szCmd));
        szWalk += 4;
    }
    else if (dwOptions & RIL_DIALOPT_PRESENTID) 
    {
        (void)strncpyz(szWalk, "*31#", MAX_PATH - (szWalk - szCmd));
        szWalk += 4;
    }
    // Copy the number in
    szAddrWalk = szAddress;
    const char * const pcszCmdEnd = &szCmd[ ARRAY_LENGTH(szCmd)-1 ];
    while( szWalk < pcszCmdEnd && *szAddrWalk != '\0' )
    {
        // Only allow characters in the set specified by GSM 07.07 section 6.2
        if (strchr("1234567890*#+ABCD,TP!W@",*szAddrWalk))
        {
            *szWalk++ = *szAddrWalk;
        }
        szAddrWalk++;
    }

    // If the dial string is really long, make a note in case the
    // command fails with a generic error. We can then guess that
    // the real error is that the dial string was too long.
    if ((szWalk - szDialStringStart) > MAX_DIAL_STRING_LENGTH)
    {
        dwDialOpts |= CMDOPT_LONGDIALSTRING;
    }

    if ( szWalk == szDialStringStart )
    {
        // No valid chars processed
        hr = E_INVALIDARG;
        goto Error;
    }

    // Remember to paste on the terminating '\0'
    *szWalk='\0';

#if !defined(WAVECOM_DRIVER) && !defined(EMP_DRIVER)
    // HW-SPECIFIC: WaveCom hardware doesn't support Closed User Groups
    if (dwOptions & RIL_DIALOPT_CLOSEDGROUP) {
        (void)strncpyz(szWalk, "G", MAX_PATH - (szWalk - szCmd));
        szWalk++;
    }
#endif // WAVECOM_DRIVER && EMP_DRIVER
/*
    if (dwOptions & RIL_DIALOPT_RESTRICTID) {
        (void)strncpyz(szWalk, "I", MAX_PATH - (szWalk - szCmd));
        szWalk++;
    }
    else if (dwOptions & RIL_DIALOPT_PRESENTID) {
        (void)strncpyz(szWalk, "i", MAX_PATH - (szWalk - szCmd));
        szWalk++;
    }
*/
#ifdef EMP_DRIVER
    // For EMP "I" and "i" should always go before "G"
    if (dwOptions & RIL_DIALOPT_CLOSEDGROUP)
    {
        (void)strncpyz(szWalk, "G", MAX_PATH - (szWalk - szCmd));
        szWalk++;
    }
    // VT <number> is a quoted string on EMP platform
    if (g_dwCallType==RIL_CALLTYPE_VT)
    {
        (void)strncpyz(szWalk, "\"", MAX_PATH - (szWalk - szCmd));
        szWalk++;
    }
#endif // EMP_DRIVER

    // If it's not a data call, terminate with a ; character
    if ((dwType==RIL_CALLTYPE_VOICE)||(dwType==RIL_CALLTYPE_PTT))
    {
        (void)strncpyz(szWalk, ";", MAX_PATH - (szWalk - szCmd));
        szWalk++;
    }
    else if (dwType==RIL_CALLTYPE_DATA)
    {
        dwDialOpts |= CMDOPT_DELAYCONNECTRSP;
    }

    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));

    // perform external calltype determination processing
    if (g_rfExternalCalltypeDetermination)
    {
        //  There is no call id associated with the the dialed call yet,
        //  so store the calltype so it can be used later
        EnterCriticalSection(&g_csDialedCallData);
        g_rcdDialedCallData.fValid = TRUE;
        g_rcdDialedCallData.dwCalltype = dwType;
        MultiByteToWideChar(CP_ACP, 0, szAddress, MAXLENGTH_ADDRESS, g_rcdDialedCallData.wszAddress, MAXLENGTH_ADDRESS);
        RETAILMSG(MSG_ON, (TEXT("RILDrv : i : RILDrv_Dial : Setting g_rcdDialedCallData calltype = %d, address = %s.\r\n"), g_rcdDialedCallData.dwCalltype, g_rcdDialedCallData.wszAddress));
        LeaveCriticalSection(&g_csDialedCallData);
    }

    if ((dwType==RIL_CALLTYPE_VOICE)||(dwType==RIL_CALLTYPE_PTT))
    {
        // Indicate call is Active
        IndicateCallActivityToAudioSubsystem(TRUE, FALSE);
    }
#if defined(WAVECOM_DRIVER)
    HoldIfAllActive();
#endif

    if (!QueueCmd(pHandle, szCmd, dwDialOpts, APIID_DIAL, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

Error:

    if ( E_FAIL == hr )
    {
        // Indicate call is inactive to audio driver only if call list is empty.
        IndicateCallActivityToAudioSubsystem(FALSE, TRUE);
    }

    return hr;
}


//
//
//
HRESULT RILDrv_Answer(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_Answer);
    CNotificationData* pnd = NULL;
    RILCONNECTINFO rci; memset(&rci,0,sizeof(rci)); // zero struct
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Set up the voice connect notification. If  incoming call is a data call,
    //    we will get a "CONNECT" instead of "OK", which will prevent this notification
    //    from being sent
    pnd = new CNotificationData;
    if (pnd) {
        rci.cbSize = sizeof(RILCONNECTINFO);
        rci.dwParams = RIL_PARAM_CNI_CALLTYPE;
#ifdef EMP_DRIVER
        // It may be a VT call on EMP platform, so use the global variable
        rci.dwCallType = g_dwCallType;
#else
        rci.dwCallType = RIL_CALLTYPE_VOICE;
#endif

        // check for externally determined calltype
        if (g_rfExternalCalltypeDetermination)
        {
            //  There may be no call id associated with the call yet,
            //  so use the previously stored calltype
            EnterCriticalSection(&g_csRingingCallData);
            if (g_rcdRingingCallData.fCalltypeValid)
            {
                rci.dwCallType = g_rcdRingingCallData.dwCalltype;
                RETAILMSG(MSG_ON, (TEXT("RILDrv : i : RILDrv_Answer : using calltype %d.\r\n"), g_rcdRingingCallData.dwCalltype));
            }
            RETAILMSG(MSG_ON, (TEXT("RILDrv : i : RILDrv_Answer : Ringing Call TypeValid = %d, type = %d, IdValid = %d, Id = %d, \r\n"), 
                g_rcdRingingCallData.fCalltypeValid, g_rcdRingingCallData.dwCalltype,g_rcdRingingCallData.fCallIdValid,g_rcdRingingCallData.dwCallId));
            LeaveCriticalSection(&g_csRingingCallData);
        }
        /*
        if (g_rfExternalCalltypeDetermination)
        {
            //  There may be no call id associated with the call yet,
            //  so use the previously stored calltype
            EnterCriticalSection(&g_csRingingCallData);
            if (g_rcdRingingCallData.fCalltypeValid)
            {
                rci.dwCallType = g_rcdRingingCallData.dwCalltype;
                RETAILMSG(MSG_ON, (TEXT("RILDrv : i : RILDrv_Answer : using calltype %d.\r\n"), g_rcdRingingCallData.dwCalltype));
                g_rcdRingingCallData.dwCalltype = RIL_CALLTYPE_UNKNOWN;
                g_rcdRingingCallData.fCalltypeValid = FALSE;
                //  Just in case there was a call id, clear the associated ringing call globals
                g_rcdRingingCallData.fCallIdValid = FALSE;
                g_rcdRingingCallData.dwCallId = RIL_MAX_TRACKED_CALL_ID;
            }
            RETAILMSG(MSG_ON, (TEXT("RILDrv : i : RILDrv_Answer : Ringing Call TypeValid = %d, type = %d, IdValid = %d, Id = %d, \r\n"), 
                g_rcdRingingCallData.fCalltypeValid, g_rcdRingingCallData.dwCalltype,g_rcdRingingCallData.fCallIdValid,g_rcdRingingCallData.dwCallId));
            LeaveCriticalSection(&g_csRingingCallData);
        }
        */
        
        if (!pnd->InitFromRealBlob(RIL_NOTIFY_CONNECT, &rci, sizeof(RILCONNECTINFO))) {
            delete pnd;
            pnd = NULL;
        }
    }
    if( rci.dwCallType == RIL_CALLTYPE_VOICE || rci.dwCallType == RIL_CALLTYPE_PTT )
    {
        // Indicate call is Active.
        IndicateCallActivityToAudioSubsystem(TRUE, FALSE);
    }
#ifdef EMP_DRIVER
    // EMP uses *EVA to answer a VT call
    if (RIL_CALLTYPE_VT == g_dwCallType) {
        if (!QueueCmd(pHandle, "AT*EVA\r", CMDOPT_ANSWER, APIID_ANSWER, NULL, pnd, hr)) {
            hr = E_FAIL;
        }
    }
    else
#endif

    if( rci.dwCallType == RIL_CALLTYPE_DATA )
    {
        RETAILMSG(MSG_ON, (TEXT("For data call, don't send connected message with OK response\r\n")));
        if (!QueueMultipartCmd(pHandle, "ATA\r", NULL, CMDOPT_ANSWER | CMDOPT_INTERMEDIATERESPONSE, APIID_ANSWER, NULL, NULL, hr))
        {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        if (!QueueCmd(pHandle, "ATA\r", CMDOPT_ANSWER, APIID_ANSWER, NULL, pnd, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    
    pnd = NULL;

Error:

    if (E_FAIL == hr) 
    {
        // Indicate call is inactive to audio driver only if call list is empty.
        IndicateCallActivityToAudioSubsystem(FALSE, TRUE);
    }

    return hr;
}


//
//
//
HRESULT RILDrv_Hangup(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_Hangup);
    CNotificationData* pnd = NULL;
    BOOL fSuccess;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    pnd = new CNotificationData;
    if (pnd) {
        if (pHandle->FPreferred()) {
            fSuccess = pnd->InitFromRealBlob(RIL_NOTIFY_EMERGENCYHANGUP, NULL, 0);
        } else {
           // must notify of disconnect here since this lets drivers go to lower power state
            fSuccess = pnd->InitFromDWORDBlob(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_LOCAL);
        }
        if (!fSuccess) {
            delete pnd;
            pnd = NULL;
        }
    }
#ifdef EMP_DRIVER
    char szCmd[MAX_PATH];
    if (RIL_CALLTYPE_VT == g_dwCallType)
    {
        // EMP uses *EVH to hangup a VT call
        strncpyz(szCmd,"AT*EVH\r",MAX_PATH);
    }
    else
    {
        strncpyz(szCmd,"ATH\r",MAX_PATH);
        // Check if there is on-hold call
        for (int i=0; i<RIL_MAX_TRACKED_CALL_ID; i++)
        {
            if ((g_rgfCallStates[i].dwParams & RIL_PARAM_CI_STATUS) && 
                (RIL_CALLSTAT_ONHOLD == g_rgfCallStates[i].dwStatus))
            {
                // Use AT+CHLD=0 to hang up on-hold calls
                // ATH can not hang up an on-hold call on EMP platform
                strncpyz(szCmd,"AT+CHLD=0\r",MAX_PATH);
                break;
            }
        }
    }
    if (!QueueCmd(pHandle, szCmd, CMDOPT_HANGUP, APIID_HANGUP, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
#else
    if (!QueueCmd(pHandle, "AT+CHUP\r", CMDOPT_HANGUP, APIID_HANGUP, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
#endif
    pnd = NULL;

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SendDTMF(DWORD dwParam, LPCSTR lpszChars, DWORD dwDuration)
{
    FUNCTION_TRACE(RILDrv_SendDTMF);
#if !defined(OEM1_DRIVER) && !defined(EMP_DRIVER) // SUPPORT_DTMF_DURATION
    UINT nValue;
#endif
    DEBUGCHK(NULL != lpszChars);
    DEBUGCHK(0 < strlen(lpszChars));

    UINT NumVals;
    DWORD dwTimeout;

    char szCmd[MAX_PATH];
    LPSTR szWalk = szCmd;
    LPCSTR pchChars;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpszChars) {
        hr = E_FAIL;
        goto Error;
    }

#if !defined(OEM1_DRIVER) && !defined(EMP_DRIVER) // SUPPORT_DTMF_DURATION
    if ((dwDuration < 300) || (dwDuration == RIL_DTMFDURATION_DEFAULT))
    {
        dwDuration = 300;
    }

    nValue = (dwDuration + 50 ) / 100;

    (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "AT+VTD=%u;", nValue);
#else
    (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "AT");
    // Because we're not setting the duration, the radio is using the default
    // duration.  Make sure we use the default value so the timeout calculation
    // below is correct.
    dwDuration = 300;
#endif
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    NumVals=0;
#if !defined(OEM2_DRIVER) && !defined(EMP_DRIVER)
    for (pchChars = lpszChars; *pchChars; pchChars++) {
        NumVals++;
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "+VTS=%c;", *pchChars);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }
    // remove the trailing ";"
    *(--szWalk) = '\0';
#else

    // OEM2 has a special version of AT+VTS which allows up to 20 DTMF tones in a single command
    // EMP also supports multiple DTMF tones in a sigle command

    pchChars = lpszChars;
    if (*pchChars)
    {
        UINT nSent = 0;
        NumVals++;
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "+VTS=\"%c", *pchChars);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
        pchChars++;
        nSent++;

        // The length of szCmd is limited by MAX_PATH(260), thus at most 107 characters can be fit into one AT command
        for (; (*pchChars) && (pchChars-lpszChars<=107); pchChars++) 
        {
            NumVals++;
            if (nSent >= 20)
            {
                (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "\";+VTS=\"%c", *pchChars);
                szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
                DEBUGCHK(NULL != szWalk);
                nSent = 1;
            }
            else
            {
                (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), ",%c", *pchChars);
                szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
                DEBUGCHK(NULL != szWalk);
                nSent++;
            }
        }
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "\"", *pchChars);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
        
    }
#endif // !defined(OEM2_DRIVER) && !defined(EMP_DRIVER)
    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));  // NO_TYPO: 30

    // Pick a timeout value to use
    dwTimeout = (2 * NumVals * dwDuration) + g_TimeoutAPIDefault;
    RETAILMSG(MSG_ON, (TEXT("RILDrv : i : RILDrv_SendDTMF : Sending DTMF, NumVals=%d, dwDuration=%d, Timeout = %d ms\r\n"), NumVals, dwDuration, dwTimeout));

    if (!QueueCmdWithTimeout(pHandle, szCmd, NULL, CMDOPT_SUPPRESSLOGGING, APIID_SENDDTMF, NULL, NULL, hr, dwTimeout,0)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
static HRESULT ParseGetCallList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetCallList);
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    //char szAddress[MAXLENGTH_ADDRESS];
    RILCALLINFO* rgrci = NULL;
    HRESULT hr = S_OK;
    BOOL bSuccess;
    BOOL bIncomingCall = FALSE;
    BOOL rgfNewCallsInProgress[RIL_MAX_TRACKED_CALL_ID] = { 0 };  // Initialize to FALSE
    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Clear callstate structure for a clean slate
    memset(g_rgfCallStates, 0x00, sizeof(g_rgfCallStates));
    if ((g_btctr_flag)&&
	(dwBTHarewareState & BTSF_A2DP_CONNECTED)&&
	((strstr(szRsp, "OK\r\n") == szRsp)||
	(strstr(szRsp, "0\r") == szRsp)))
    {
        g_btctr_flag = FALSE;
        RETAILMSG(1,(TEXT("Call disconnect dwBTHarewareState:%X\r\n"),dwBTHarewareState));  
        if(!A2DPIoControl(WODM_OPEN_CLOSE_A2DP, WODM_PARAM_OPEN_A2DP)) 
        {
            RETAILMSG(1,(TEXT("A2DPIoControl open fail\r\n")));  
        }
    }			


    // Parse "+CLCC: "
    while (MatchStringBeginning(szRsp, "+CLCC: ", szRsp)) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrci, sizeof(RILCALLINFO), nUsed, &nAllocated, CALLCTRL_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrci[nUsed], 0x00, sizeof(RILCALLINFO));
        rgrci[nUsed].cbSize = sizeof(RILCALLINFO);
        rgrci[nUsed].raAddress.cbSize = sizeof(RILADDRESS);

        // Parse "<id>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        rgrci[nUsed].dwID = nValue;
        rgrci[nUsed].dwParams |= RIL_PARAM_CI_ID;

        // Track the call
        DEBUGCHK((0 <= nValue) && (nValue < ARRAY_LENGTH(rgfNewCallsInProgress))); // Otherwise, it may be necessary to increase RIL_MAX_TRACKED_CALL_ID
        if ((0 <= nValue) && (nValue < ARRAY_LENGTH(rgfNewCallsInProgress))) {
            rgfNewCallsInProgress[nValue] = TRUE;
        }

        // Parse ",<direction>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp)) {
            goto Continue;
        }
        rgrci[nUsed].dwDirection = (nValue ? RIL_CALLDIR_INCOMING : RIL_CALLDIR_OUTGOING);
        rgrci[nUsed].dwParams |= RIL_PARAM_CI_DIRECTION;

        // Parse ",<status>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUIntAndVerifyAbove(szRsp, TRUE, NUM_CALLSTATS, nValue, szRsp)) {
            goto Continue;
        }
        if(nValue == 4)
        {
            if(!g_btctr_flag)
            {
                dwBTHarewareState = GetBTState();	
                if (dwBTHarewareState & BTSF_A2DP_CONNECTED)
                {
                    g_btctr_flag = TRUE;
                    RETAILMSG(1,(TEXT("incoming call  dwBTHarewareState:%X\r\n"),dwBTHarewareState));  
                    if(!A2DPIoControl(WODM_OPEN_CLOSE_A2DP, WODM_PARAM_CLOSE_A2DP))
                    {
                        RETAILMSG(1,(TEXT("A2DPIoControl close fail\r\n")));  
                    }
                }
            }
        }
        rgrci[nUsed].dwStatus = g_rgdwCallStats[nValue];
        rgrci[nUsed].dwParams |= RIL_PARAM_CI_STATUS;

        switch (g_rgdwCallStats[nValue])
        {
        case RIL_CALLSTAT_INCOMING:
        case RIL_CALLSTAT_WAITING:
            bIncomingCall = TRUE;
        }

        // Parse ",<type>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUIntAndVerifyAbove(szRsp, TRUE, NUM_CALLTYPES, nValue, szRsp)) {
            goto Continue;
        }
        rgrci[nUsed].dwType = g_rgdwCallTypes[nValue];
#ifdef EMP_DRIVER
        // On EMP platform VT call has the same type as data call
        if (RIL_CALLTYPE_VT == g_dwCallType && RIL_CALLTYPE_DATA == rgrci[nUsed].dwType)
        {
            rgrci[nUsed].dwType = RIL_CALLTYPE_VT;
        }
#endif
        rgrci[nUsed].dwParams |= RIL_PARAM_CI_TYPE;

        // Parse ",<multiparty>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp)) {
            goto Continue;
        }
        rgrci[nUsed].dwMultiparty = (nValue ? RIL_CALL_MULTIPARTY : RIL_CALL_SINGLEPARTY);
        rgrci[nUsed].dwParams |= RIL_PARAM_CI_MULTIPARTY;
/*
        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp)) {
            // Parse "<address>,<type>"
            if (ParseString(szRsp, szAddress, MAXLENGTH_ADDRESS, szRsp)) {
                if (!MatchStringBeginning(szRsp, ",", szRsp)                 ||
                    !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp)  ||
                    !StringToRILAddress(szAddress, (BYTE)nValue, rgrci[nUsed].raAddress)) {
                    goto Continue;
                }
                rgrci[nUsed].dwParams |= RIL_PARAM_CI_ADDRESS;
            }
            else {
                // If we couldn't parse an address, then it might be empty,
                // meaning the ID is blocked. Since the address parameter
                // is present, make sure the type also exists before continuing.
                if (!MatchStringBeginning(szRsp, ",", szRsp)                 ||
                    !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp)) {
                    goto Continue;
                }
            }

            // Parse ","
            if (MatchStringBeginning(szRsp, ",", szRsp)) {
                // Parse "<description>"
                if (!ParseQuotedEncodedString(ENCODING_TECHARSET, szRsp, rgrci[nUsed].wszDescription, rgrci[nUsed].wszDescription + MAXLENGTH_DESCRIPTION)) {
                    goto Continue;
                }
                rgrci[nUsed].dwParams |= RIL_PARAM_CI_DESCRIPTION;
            }
#ifdef EMP_DRIVER
            // Parse ",<priority>" (0-4)
            if (!MatchStringBeginning(szRsp, ",", szRsp) ||
                !ParseUIntAndVerifyAbove(szRsp, TRUE, 5, nValue, szRsp)) {
                goto Continue;
            }
#endif
        }
*/
        // copy the current call info into our global array
        g_rgfCallStates[rgrci[nUsed].dwID] = rgrci[nUsed];

        // do external calltype progessing for this call
        if (g_rfExternalCalltypeDetermination)
        {
            // get the calltype and associate it with the call id
            SetCalltypeFromCallInfo(&rgrci[nUsed]);
        }

        // Increment the array index
        nUsed++;

Continue:
        // Find "<postfix>"
        bSuccess = ParseRspPostfix(szRsp, szRsp);

        // Note: WaveCom euro radios forget to include the <cr><lf> between +CLCC lines,
        // so for wavecom don't worry if we don't find these characters
#ifndef WAVECOM_DRIVER
        if (!bSuccess)
        {
            hr = E_FAIL;
            goto Error;
        }
#endif


    }

RETAILMSG(MSG_ON, (TEXT("RILDrv : i : ParseGetCallList : sizeof(rgfNewCallsInProgress) = %d \r\n"), sizeof(rgfNewCallsInProgress)));

    // Now that we've succeeded parsing the notification, record the new call states
    C_ASSERT(sizeof(g_rgfCallsInProgress) == sizeof(rgfNewCallsInProgress));
    CopyMemory(g_rgfCallsInProgress, rgfNewCallsInProgress, sizeof(g_rgfCallsInProgress));

    // clean up externally determined calltypes, if necessary
    if (g_rfExternalCalltypeDetermination)
    {
        UINT uiRilCall = 0;
        while(uiRilCall < RIL_MAX_TRACKED_CALL_ID)
        {
            if (FALSE == rgfNewCallsInProgress[uiRilCall])
            {
                // no current call with this id, make sure that the calltype tracking data is erased
                g_rgfCalltypeChecked[uiRilCall] = FALSE;
                g_rgctCalltype[uiRilCall] = RIL_CALLTYPE_UNKNOWN;
                //  Was this a ringing call?
                EnterCriticalSection(&g_csRingingCallData);
                if ((TRUE == g_rcdRingingCallData.fCallIdValid) && (uiRilCall == g_rcdRingingCallData.dwCallId))
                {
                    g_rcdRingingCallData.fCallIdValid = FALSE;
                    g_rcdRingingCallData.dwCallId = RIL_MAX_TRACKED_CALL_ID;
                    g_rcdRingingCallData.dwCalltype = RIL_CALLTYPE_UNKNOWN;
                    g_rcdRingingCallData.fCalltypeValid = FALSE;
                    RETAILMSG(MSG_ON, (TEXT("RILDrv : i : ParseGetCallList : Ringing Call TypeValid = %d, type = %d, IdValid = %d, Id = %d, \r\n"), 
                        g_rcdRingingCallData.fCalltypeValid, g_rcdRingingCallData.dwCalltype,g_rcdRingingCallData.fCallIdValid,g_rcdRingingCallData.dwCallId));
                }
                LeaveCriticalSection(&g_csRingingCallData);
            }
            uiRilCall++;
        }
    }

    SetBacklightIncomingCall(bIncomingCall);

    pBlob = (void*)rgrci;
    cbBlob = nUsed * sizeof(RILCALLINFO);

Error:

    if( nUsed == 0 )
    {
        // Indicate call is inactive to audio driver.
        IndicateCallActivityToAudioSubsystem(FALSE, FALSE);
    }

    if (FAILED(hr)) {
        FreeBlob(rgrci);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetCallList(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetCallList);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef RIL_RADIO_RESILIENCE
    if (ShouldSpoofCommand())
    {
        // If the radio was reset recently, just return no calls.  Otherwise this may get
        // queued up after all the init commands, and take a long time to execute, only
        // to find out that there are no calls.  This is bad, since we want to handle
        // the RIL_NOTIFY_DISCONNECT that is sent up during reset so the UI can be
        // updated quickly
        hr = pHandle->GetDevice()->GetNextCmdID();
        pHandle->Notify(RIL_RESULT_OK, hr, NULL, 0);
    }
    else
#endif // RIL_RADIO_RESILIENCE        
    {
        if (!QueueCmd(pHandle, "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, ParseGetCallList, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
Error:
    return hr;
}


//
//
//
HRESULT RILDrv_ManageCalls(DWORD dwParam, DWORD dwCommand, DWORD dwID)
{
    FUNCTION_TRACE(RILDrv_ManageCalls);
    CNotificationData* pnd = NULL;
    BOOL fNeedNotification = TRUE;
    BOOL fNeedSetMuting = FALSE;
    char szCmd[MAX_PATH];
    DWORD dwOpt = CMDOPT_NONE;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    switch (dwCommand)
    {
        case RIL_CALLCMD_RELEASEHELD:
#ifdef OEM2_DRIVER
            (void)strncpyz(szCmd, "AT+CHLD=7\r", MAX_PATH);
#else
            (void)strncpyz(szCmd, "AT+CHLD=0\r", MAX_PATH);
#endif
            dwOpt |= CMDOPT_HANGUP;  // Necessary when attempting to end a single outgoing call
            break;

        case RIL_CALLCMD_RELEASEACTIVE_ACCEPTHELD:
            (void)strncpyz(szCmd, "AT+CHLD=1\r", MAX_PATH);
            break;

        case RIL_CALLCMD_RELEASECALL:
            if (dwID==0)
            {
                // Id of 0 is a magic value which says to release a call being dialed while
                // another call is on hold.
                // This activates code in HangupThreadProc to force it to send AT\r to the modem
                // to try to abort a call being dialed.
#if defined (WAVECOM_DRIVER)
                // H1 on wavecom will hang up calls in the alerting state.
                (void)strncpyz(szCmd, "ATH1\r", MAX_PATH);
                dwOpt = CMDOPT_HANGUP;
#else
                // We don't need to send a command to the modem because 
                // HangupThreadProc's AT\r will cancel the dial.
                (void)strncpyz(szCmd, "AT\r", MAX_PATH);
                dwOpt = CMDOPT_HANGUP | CMDOPT_NOOP;
#endif
            }
            else if (9 == dwID)
            {
                // This is a special-case to enable us to pass FTA case GSM 51.010 31.4.4.2 which requires
                // a way to end all calls in one operation (i.e., "19<SEND>")
                (void)strncpyz(szCmd, "ATH\r", MAX_PATH);
            }
            else if (8 == dwID)
            {
                // This is a special-case to enable us to pass FTA case GSM 51.010 31.4.4.1.2.4 which requires
                // a way to end a held conference call in one operation (i.e., "18<SEND>")
                (void)strncpyz(szCmd, "at+chld=11\rAT\rat+chld=12\r", MAX_PATH);
            }
            else
            {
                DEBUGCHK((0 <= dwID) && (dwID < ARRAY_LENGTH(g_rgfCallsInProgress)));  // Otherwise, if may be necessary to increase MAX_TRACKED_CALLS
                if ((0 <= dwID) && (dwID < ARRAY_LENGTH(g_rgfCallsInProgress)) && g_rgfCallsInProgress[dwID])
                {
#ifdef OEM1_DRIVER
                    (void)_snprintfz(szCmd, MAX_PATH, "AT%%CHLD=7%u\r", dwID);
#else
                    (void)_snprintfz(szCmd, MAX_PATH, "AT+CHLD=1%u\r", dwID);
#endif
#if defined (WAVECOM_DRIVER)
                    // We need special case for data calls. We assume that
                    // the data call is always line 1.
                    // When a data call is released, we need to make sure that
                    // RIL gets out of data mode appropriately.
                    if (1 == dwID)
                    {
                        dwOpt |= CMDOPT_HANGUP;
                    }
#endif
                }
                else
                {
                    // Special case for FTA case GSM 51.010 31.9.1.1.16 which requires us to send the command as USSD
                    // if there isn't an active call associated with this ID
                    (void)_snprintfz(szCmd, MAX_PATH, "AT+CUSD=1,\"1%u\",15\r", dwID);
                    dwOpt |= CMDOPT_SUPPRESSLOGGING;
                    fNeedNotification = FALSE;
                }
            }
              break;

        case RIL_CALLCMD_HOLDACTIVE_ACCEPTHELD:
#ifdef OEM2_DRIVER
            (void)strncpyz(szCmd, "AT+CHLD=6\r", MAX_PATH);
#else
            (void)strncpyz(szCmd, "AT+CHLD=2\r", MAX_PATH);
            fNeedSetMuting = TRUE;
#endif
            break;

        case RIL_CALLCMD_HOLDALLBUTONE:
            DEBUGCHK((0 <= dwID) && (dwID < ARRAY_LENGTH(g_rgfCallsInProgress)));  // Otherwise, it may be necessary to increase MAX_TRACKED_CALLS
            if ((0 <= dwID) && (dwID < ARRAY_LENGTH(g_rgfCallsInProgress)) && g_rgfCallsInProgress[dwID])
            {
#if defined(WAVECOM_DRIVER)
                ActiveIFALLHOLD();
#endif
                (void)_snprintfz(szCmd, MAX_PATH, "AT+CHLD=2%u\r", dwID);
                fNeedSetMuting = TRUE;
            }
            else
            {
                // Special case for FTA case GSM 51.010 31.9.1.1.16 which requires us to send the command as USSD
                // if there isn't an active call associated with this ID
                dwOpt |= CMDOPT_SUPPRESSLOGGING;
                (void)_snprintfz(szCmd, MAX_PATH, "AT+CUSD=1,\"2%u\",15\r", dwID);
            }

            fNeedNotification = FALSE;
            break;

        case RIL_CALLCMD_ADDHELDTOCONF:
            (void)strncpyz(szCmd, "AT+CHLD=3\r", MAX_PATH);
            fNeedNotification = FALSE;
            break;

        case RIL_CALLCMD_ADDHELDTOCONF_DISCONNECT:
            (void)strncpyz(szCmd, "AT+CHLD=4\r", MAX_PATH);
            break;

        case RIL_CALLCMD_INVOKECCBS:
#ifdef EMP_DRIVER
            hr = E_NOTIMPL;
            goto Error;
#else
            (void)strncpyz(szCmd, "AT+CHLD=5\r", MAX_PATH);
            fNeedNotification = FALSE;
#endif
            break;

        default:
            // The proxy code should never let us get here
            DEBUGCHK(FALSE);
    }

    if (fNeedNotification) {
        pnd = new CNotificationData;
        if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_CALLSTATECHANGED, NULL, 0)) {
            delete pnd;
            pnd = NULL;
        }
    }

    if (!QueueCmd(pHandle, szCmd, dwOpt, APIID_MANAGECALLS, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;
#ifdef WAVECOM_DRIVER
    // If muting has been turned on, sending command "+CMUT=1" every time call is retrieved. 
    if ( fNeedSetMuting && g_fAudioMutingOn ) 
    {
        QueueCmdIgnoreRsp(APIID_NONE, "AT+CMUT=1\r", CMDOPT_NONE, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0);
    }
#endif
Error:
    return hr;
}


//
//
//
HRESULT RILDrv_TransferCall(DWORD dwParam, const RILADDRESS* lpAddress, const RILSUBADDRESS* lpSubAddress)
{
    FUNCTION_TRACE(RILDrv_TransferCall);
#if !defined(WAVECOM_DRIVER) && !defined(EMP_DRIVER)
    // HW-SPECIFIC: WaveCom and EMP hardware don't support AT+CTFR

    BYTE bTypeOfAddress;
    BYTE bType;
    char szAddress[MAXLENGTH_ADDRESS];
    char szSubAddress[MAXLENGTH_SUBADDR];
    char szCmd[CALLCTRL_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    HRESULT hrSubAddress;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpAddress) {
        hr = E_FAIL;
        goto Error;
    }

    // Determine address and type-of-address byte
    hr = RILAddressToString(*lpAddress, szAddress, MAXLENGTH_ADDRESS, bTypeOfAddress);
    if (FAILED(hr)) {
        goto Error;
    }
    (void)_snprintfz(szWalk, CALLCTRL_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CTFR=\"%s\",%u", szAddress, bTypeOfAddress);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (lpSubAddress) {
        hrSubAddress = RILSubAddressToString(*lpSubAddress, szSubAddress, MAXLENGTH_SUBADDR, bType);
        if (SUCCEEDED(hrSubAddress)) {
            (void)_snprintfz(szWalk, CALLCTRL_CMDBUF_LENGTH - (szWalk - szCmd), ",\"%s\",%u", szSubAddress, bType);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }
    }
    (void)strncpyz(szWalk, "\r", CALLCTRL_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30

    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_TRANSFERCALL, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
#else  // WAVECOM_DRIVER && EMP_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // WAVECOM_DRIVER && EMP_DRIVER

    return hr;
}


//
//
//
static HRESULT ParseGetLineStatusRsp(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetLineStatusRsp);
    UINT nValue;
    DWORD* pdwLineStatus = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwLineStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwLineStatus) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwLineStatus, 0x00, sizeof(DWORD));

    // Parse "<prefix>+CPAS: <line_status><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                  ||
        !MatchStringBeginning(szRsp, "+CPAS: ", szRsp)                 ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, NUM_LINESTATS, nValue, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    *pdwLineStatus = g_rgdwLineStats[nValue];

    SetBacklightIncomingCall(RIL_LINESTAT_RINGING == g_rgdwLineStats[nValue]);

#if defined(WAVECOM_DRIVER_WAVEXT)
    //Activate the speaker if line state is ringing.
    IndicateForceSpeakerToAudioSubsystem ( RIL_LINESTAT_RINGING == g_rgdwLineStats[nValue] );
#endif

    pBlob = (void*)pdwLineStatus;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwLineStatus);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetLineStatus(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetLineStatus);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef RIL_RADIO_RESILIENCE
    if (ShouldSpoofCommand())
    {
        // If the radio was reset recently, just return idle line status.  Otherwise this may get
        // queued up after all the init commands, and take a long time to execute, only
        // to find out that there are no calls.  This is bad, since we want to handle
        // the RIL_NOTIFY_DISCONNECT that is sent up during reset so the UI can be
        // updated quickly        
        DWORD* pdwLineStatus = (DWORD*)AllocBlob(sizeof(DWORD));
        if (!pdwLineStatus) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }
        hr = pHandle->GetDevice()->GetNextCmdID();
        *pdwLineStatus = RIL_LINESTAT_READY;
        pHandle->Notify(RIL_RESULT_OK, hr, pdwLineStatus, sizeof(DWORD));
    }
    else
#endif // RIL_RADIO_RESILIENCE
    {
        if (!QueueCmd(pHandle, "AT+CPAS\r", CMDOPT_NONE, APIID_GETLINESTATUS, ParseGetLineStatusRsp, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetCurrentAddressId(DWORD dwParam, DWORD dwAddressId)
{
    FUNCTION_TRACE(RILDrv_SetCurrentAddressId);
#if defined(OEM1_DRIVER) || defined(EMP_DRIVER)
    HRESULT hr = S_OK;
    char szCmd[MAX_PATH];
    CNotificationData* pnd = NULL;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef EMP_DRIVER
    // EMP only supports 1 for <line>
    if (1 != dwAddressId)
    {
        hr = E_NOTIMPL;
        goto Error;
    }
#endif

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_CURRENTLINECHANGED, dwAddressId)) {
        delete pnd;
        pnd = NULL;
    }

#ifdef EMP_DRIVER
    (void)_snprintfz(szCmd, MAX_PATH, "AT*ELIN=%u\r", dwAddressId);
#else
    (void)_snprintfz(szCmd, MAX_PATH, "AT%%ALS=%u\r", dwAddressId);
#endif
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SETCURRENTADDRESSID, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
#else
    HRESULT hr = E_NOTIMPL;
#endif
    return hr;
}

//
//
//
static HRESULT ParseGetCurrentAddressId(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetCurrentAddressId);
    UINT nValue;
    DWORD* pdwAddressId = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwAddressId = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwAddressId) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    *pdwAddressId = 0;

    // Parse "<prefix>%ALS: <addressID><postfix>"
    // On EMP platform the response is "<prefix>*ELIN: <addressID><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                   ||
#ifdef EMP_DRIVER
        !MatchStringBeginning(szRsp, "*ELIN: ", szRsp)   ||
#else
        !MatchStringBeginning(szRsp, "%ALS: ", szRsp)   ||
#endif
        !ParseUInt(szRsp, TRUE, nValue, szRsp)          ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    *pdwAddressId = nValue;

    pBlob = (void*)pdwAddressId;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwAddressId);
    }
    return hr;
}

//
//Active all call if all calls are hold
//
void ActiveIFALLHOLD()
{
    UINT iActivecalls =0;
    UINT iHoldcalls = 0;
    UINT iCallsInproceeding =0;
    
    for(int i = 0; i < RIL_MAX_TRACKED_CALL_ID; i++)
    {
        if (RIL_PARAM_CI_CPISTATUS & g_rgfCallStates[i].dwParams)
        {
            switch (g_rgfCallStates[i].dwStatus)
            {
                case RIL_CPISTAT_CONNECTED:
                    iActivecalls++;
                    break;
                case RIL_CPISTAT_NEW_INCOMING:
                case RIL_CPISTAT_NEW_OUTGOING:
                    iCallsInproceeding++;
                    break;
                /*case RIL_CPISTAT_UNKNOWN:
                    if ( TRUE == g_rgfTCHAssigned[1])
                        iCallsInproceeding++;
                    break;*/
                default:
                    break;
            }
        }
        else if (RIL_PARAM_CI_STATUS & g_rgfCallStates[i].dwParams)
        {
            switch (g_rgfCallStates[i].dwStatus)
            {
                case RIL_CALLSTAT_ACTIVE:      
                    iActivecalls++;
                    break;
                case RIL_CALLSTAT_ONHOLD:  
                    iHoldcalls++;
                    break;

                case RIL_CALLSTAT_DIALING: 
                case RIL_CALLSTAT_ALERTING: 
                case RIL_CALLSTAT_INCOMING: 
                case RIL_CALLSTAT_WAITING:
                    iCallsInproceeding++;
                    break;

                default:
                    break;
            }	
        }
    }

	//Active if all are in hold status
    if (iHoldcalls>0 && iActivecalls ==0 && iCallsInproceeding ==0 )
    {
        QueueCmdIgnoreRsp(APIID_NONE, "AT+CHLD=2\r", CMDOPT_NONE, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0);
    }
}

//
//Hold all call if all calls are avtive
//
void HoldIfAllActive()
{
    UINT iActivecalls =0;
    UINT iHoldcalls = 0;
    UINT iCallsInproceeding =0;
    
    for(int i = 0; i < RIL_MAX_TRACKED_CALL_ID; i++)
    {
        if (RIL_PARAM_CI_CPISTATUS & g_rgfCallStates[i].dwParams)
        {
            switch (g_rgfCallStates[i].dwStatus)
            {
                case RIL_CPISTAT_CONNECTED:
                    iActivecalls++;
                    break;
                case RIL_CPISTAT_NEW_INCOMING:
                case RIL_CPISTAT_NEW_OUTGOING:
                    iCallsInproceeding++;
                    break;
                /*case RIL_CPISTAT_UNKNOWN:
                    if ( TRUE == g_rgfTCHAssigned[1])
                        iCallsInproceeding++;
                    break;*/
                default:
                    break;
            }
        }
        else if ((RIL_PARAM_CI_STATUS & g_rgfCallStates[i].dwParams))
        {
            switch (g_rgfCallStates[i].dwStatus)
            {
                case RIL_CALLSTAT_ACTIVE:      
                    iActivecalls++;
                    break;
                case RIL_CALLSTAT_ONHOLD:  
                    iHoldcalls++;
                    break;

                case RIL_CALLSTAT_DIALING: 
                case RIL_CALLSTAT_ALERTING: 
                case RIL_CALLSTAT_INCOMING: 
                case RIL_CALLSTAT_WAITING:
                    iCallsInproceeding++;
                    break;

                default:
                    break;
            }	
        }
    }

	//Hold if all are in active status
    if (iHoldcalls==0 && iActivecalls >0 && iCallsInproceeding ==0 )
    {
        g_fHoldBeforDail = TRUE;
        QueueCmdIgnoreRsp(APIID_NONE, "AT+CHLD=2\r", CMDOPT_INIT, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0);
    }
}
//
//
//
HRESULT RILDrv_GetCurrentAddressId(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetCurrentAddressId);
#if defined(OEM1_DRIVER) || defined(EMP_DRIVER)
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef EMP_DRIVER
    if (!QueueCmd(pHandle, "AT*ELIN?\r", CMDOPT_NONE, APIID_GETCURRENTADDRESSID, ParseGetCurrentAddressId, NULL, hr)) {
#else
    if (!QueueCmd(pHandle, "AT%ALS?\r", CMDOPT_NONE, APIID_GETCURRENTADDRESSID, ParseGetCurrentAddressId, NULL, hr)) {
#endif
        hr = E_FAIL;
        goto Error;
    }

Error:
#else
    HRESULT hr = E_NOTIMPL;
#endif
    return hr;
}
BOOL A2DPIoControl(DWORD Msg, DWORD op)
{
     HANDLE  g_hWavDev;
     MMDRV_MESSAGE_PARAMS pParams;
     DWORD dwResult = NULL;
     BOOL ret = FALSE;
     g_hWavDev = CreateFile(TEXT("WAV8:"),GENERIC_READ | GENERIC_WRITE,
     						0, NULL,OPEN_EXISTING,0, NULL);
     
     if (g_hWavDev == INVALID_HANDLE_VALUE)
     {
        RETAILMSG(1,(TEXT("g_hWavDev:%X\r\n"),g_hWavDev));  
        return FALSE;
     }
     
     pParams.uDeviceId = 0;
     pParams.uMsg = Msg;
     pParams.dwUser = NULL;
     pParams.dwParam1 = op;
     pParams.dwParam2 = NULL;
     
     if (DeviceIoControl(g_hWavDev, IOCTL_WAV_MESSAGE, &pParams, sizeof(pParams), NULL, NULL, &dwResult, NULL))
     {
	 ret = TRUE;
     }
     else
     {
        RETAILMSG(1,(TEXT(" DeviceIoControl failed \r\n")));  
	 ret = FALSE;
     }
     if (g_hWavDev)
     {
        CloseHandle(g_hWavDev);
     }
     return ret;
}
DWORD GetBTState(void)
{
    HKEY   hResult = NULL;
    DWORD  DataType = REG_DWORD;
    DWORD  DataSize = sizeof(REG_DWORD);
    DWORD  dwRegValue = 0;
    if (ERROR_SUCCESS!=RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
    			 L"System\\State\\Hardware", 
    			 0, 
    			 0, 
    			 &hResult ))
    {
        RETAILMSG(1, (L"RegOpenKeyEx System State Hardware fail!!! \r\n"));
    }
    if((hResult != NULL) && (ERROR_SUCCESS ==RegQueryValueEx( hResult, 
    				 L"Bluetooth", 
    				 NULL, 
    				 &DataType, 
    				 (LPBYTE)&dwRegValue, 
    				 &DataSize)))
    {
        RETAILMSG(1, (L"RegQueryValueEx sucess \r\n"));
    }
    if(hResult!= NULL)
    {
        RegCloseKey(hResult); 
    }
    return dwRegValue;
}




